//认证模块：负责处理用户注册、登录和认证状态的管理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "auth.h"
#include "utils.h"
#include <sys/socket.h>
#include <unistd.h>
#include <stdbool.h>

char username[USERNAME_LEN];    // 存储用户输入的用户名
char password[PASSWORD_LEN];    // 存储用户输入的密码
bool is_authenticated = false;  // 用户认证状态，初始为 false（未认证）

/**
 * 显示认证菜单，为用户提供操作选项。
 * 
 * 该函数会在控制台打印出一个认证菜单，包含注册新用户、登录和退出三个选项，
 * 并提示用户输入对应的操作编号。
 */
void show_auth_menu() {
    // 打印菜单标题
    printf("\n===== 聊天室认证系统 =====\n");
    // 打印注册新用户选项
    printf("1. 注册新用户\n");
    // 打印登录选项
    printf("2. 登录\n");
    // 打印退出选项
    printf("3. 退出\n");
    // 打印菜单底部边界
    printf("==========================\n");
    // 提示用户选择操作
    printf("请选择操作: ");
}

/**
 * 处理用户注册请求。
 * 
 * 该函数会提示用户输入用户名和密码，检查用户名是否包含非法字符，
 * 若合法则向服务器发送注册请求，最后根据服务器响应输出注册结果。
 * 
 * sockfd 与服务器通信的套接字文件描述符。
 */
void handle_register(int sockfd) {
    // 用于存储发送和接收数据的缓冲区
    char buffer[BUFFER_SIZE];
    
    // 提示用户输入用户名
    printf("请输入用户名: ");
    // 从标准输入读取用户名，stdin表示标准输入流
    fgets(username, sizeof(username), stdin);
    
    // 从字符串开头开始查找，统计连续不包含换行符的字符数
    // 若存在换行符，返回其在字符串中的索引位置
    // 若不存在换行符，返回字符串的长度
    //返回值定位到username数组中对应的位置，即换行符的位置
    //将这个位置赋值为0，即\0，实现删除换行符
    username[strcspn(username, "\n")] = 0;
    
    // 检查用户名是否包含非法字符
    if (!is_valid_username(username)) {
        // 若包含非法字符，提示用户并返回
        printf("用户名包含非法字符(*#/&@￥)，请重新输入\n");
        return;
    }
    
    // 提示用户输入密码
    printf("请输入密码: ");
    // 从标准输入读取密码
    fgets(password, sizeof(password), stdin);
    // 去除密码末尾的换行符
    password[strcspn(password, "\n")] = 0;
    
    // 构造注册请求消息
    snprintf(buffer, sizeof(buffer), "/register %s %s", username, password);
    // 向服务器发送注册请求
    send(sockfd, buffer, strlen(buffer), 0);
    
    // 清空缓冲区，准备接收服务器响应
    memset(buffer, 0, BUFFER_SIZE);
    // 从服务器接收响应消息
    recv(sockfd, buffer, BUFFER_SIZE, 0);
    
    // 使用 strstr 函数检查服务器响应消息中是否包含 "失败" 关键字
    // strstr 函数会在 buffer 中查找 "失败" 第一次出现的位置
    // 若找到，返回指向该位置的指针；若未找到，返回 NULL
    if (strstr(buffer, "失败") != NULL) {
        // 若包含 "失败" 关键字，说明注册请求失败
        // 打印服务器返回的响应消息，告知用户失败原因
        printf("%s\n", buffer);
    } 
    // 若不包含 "失败" 关键字，继续检查是否包含 "成功" 关键字
    else if (strstr(buffer, "成功") != NULL) {
        // 若包含 "成功" 关键字，说明注册请求成功
        // 打印服务器返回的响应消息，告知用户注册成功
        printf("%s\n", buffer);
    }
}

/**
 *   处理用户登录请求。
 * 
 * 该函数会提示用户输入用户名和密码，然后向服务器发送登录请求，
 * 接着等待服务器响应，并根据响应结果更新用户认证状态。
 * 
 *   sockfd 与服务器通信的套接字文件描述符。
 */
void handle_login(int sockfd) {
    // 用于存储发送和接收数据的缓冲区
    char buffer[BUFFER_SIZE];
    
    // 提示用户输入用户名
    printf("请输入用户名: ");
    // 从标准输入（通常是键盘）读取一行字符串，并将其存储到 username 数组中
    // - username：指向用于存储读取字符串的字符数组的指针
    // - sizeof(username)：指定读取的最大字符数，包含字符串结束符 '\0'，防止缓冲区溢出
    // - stdin：指定输入流为标准输入
    fgets(username, sizeof(username), stdin);
    // 去除用户名末尾的换行符
    username[strcspn(username, "\n")] = 0;
    
    // 提示用户输入密码
    printf("请输入密码: ");
    // 从标准输入读取密码
    fgets(password, sizeof(password), stdin);
    // 去除密码末尾的换行符
    password[strcspn(password, "\n")] = 0;
    
    // 构造登录请求消息
    snprintf(buffer, sizeof(buffer), "/login %s %s", username, password);
    // 向服务器发送登录请求
    send(sockfd, buffer, strlen(buffer), 0);
    
    // 清空缓冲区，准备接收服务器响应
    memset(buffer, 0, BUFFER_SIZE);
    // 从服务器接收响应消息
    recv(sockfd, buffer, BUFFER_SIZE, 0);
    
    // 检查服务器响应是否包含 "成功" 关键字
    if (strstr(buffer, "成功") != NULL) {
        // 若包含 "成功"，打印服务器响应
        printf("%s\n", buffer);
        // 将用户认证状态设置为已认证
        is_authenticated = true;
    } else {
        // 若不包含 "成功"，打印服务器响应
        printf("%s\n", buffer);
    }
}

/**
 * 检查用户名是否包含非法字符。
 * 
 * 该函数遍历传入的用户名，检查其中是否包含指定的非法字符（*#/&）。
 * 如果包含非法字符，返回 false；否则返回 true。
 * 
 * name 待检查的用户名，以 null 结尾的字符串。
 * return bool 若用户名不包含非法字符返回 true，包含则返回 false。
 */
bool is_valid_username(const char *name) {
    // 定义包含非法字符的字符串，将所有非法字符存储在一个字符串中
    const char *invalid_chars = "*#/&@￥";
    // 使用 for 循环遍历用户名中的每个字符
    // i 从 0 开始，每次递增 1，直到 i 等于用户名的长度，即遍历完整个用户名
    size_t length = strlen(name);
    for (size_t i = 0; i < length; i++) {
        // 使用 strchr 函数在 invalid_chars 中查找当前用户名中的字符 name[i]
        // strchr 函数会返回该字符首次出现的位置指针，如果未找到则返回 NULL
        if (strchr(invalid_chars, name[i]) != NULL) {
            // 若找到，说明当前字符是非法字符，函数提前返回 false
            return false;
        }
    }
    // 若遍历完整个用户名都没有发现非法字符，函数返回 true，表示用户名合法
    return true;
}